package com.kalua.list.harvester.resultprocessing;

import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;
import com.kalua.list.dbaccess.product.ProductDAO;
import com.kalua.list.domainmodel.NumericId;
import com.kalua.list.domainmodel.product.Product;
import com.kalua.list.domainmodel.retailer.CrawlBatchInfo;
import com.kalua.list.harvester.errorhandling.ProcessingFailureHandler;
import com.kalua.list.harvester.parser.ProductParseResult;

public class ResultProcessorImpl implements ResultProcessor {
	
	private static final Logger logger = LoggerFactory.getLogger(ResultProcessorImpl.class);
	
	private CrawlBatchInfo batchInfo;
	private ProductDAO productDAO;
	private ProcessingFailureHandler processingFailureHandler;
	
	private Map<NumericId, Product> toCreate;
	private Map<NumericId, Product> toUpdate;
	private Map<NumericId, Product> retailerProductSet;

	public ResultProcessorImpl(	CrawlBatchInfo batchInfo, 
												ProductDAO productDAO,
												ProcessingFailureHandler processingFailureHandler) {
		this.batchInfo = batchInfo;
		this.productDAO = productDAO;
		this.processingFailureHandler = processingFailureHandler;
		this.toCreate = Maps.newConcurrentMap();
		this.toUpdate = Maps.newConcurrentMap();
		
		this.retailerProductSet=productDAO.findByRetailerId(batchInfo.getRetailerId());
	}

	@Override
	public void accept(ProductParseResult parseResult) {
		logger.debug("Processing {} result, isFailure {}, with {} products.", 
				parseResult.getVendorLinkProductId(),
				parseResult.isConnectionFailed(),
				!parseResult.isConnectionFailed()? parseResult.getProducts().size() : 0);
		
		if (parseResult.isConnectionFailed()) {
			this.processingFailureHandler.processCrawlError(parseResult);
		} else {
			Set<Product> products = parseResult.getProducts();
			for (Product product : products) {
				if (isViable(product))
					processProduct(product);
				else this.batchInfo.incrementNonViableCount();
			}					
		}
	}
	
	@Override
	public void writeCollectedResults() {
		logger.info(	"Will create {} new products, and update {} existing ones.",
							this.toCreate.size(), this.toUpdate.size());		
		if (!this.toCreate.isEmpty())
			this.productDAO.create(this.toCreate);
		
		if (!this.toUpdate.isEmpty())
			this.productDAO.update(this.toUpdate);
		
		logger.info("Results  successfully written to persistent store.");
	}
	
	protected boolean isViable(Product product) {
		boolean result = true;		
		if (StringUtils.isEmpty(product.getName()) ||
				product.getPrice() <= 0d)
			result = false;
		return result;
	}

	protected CrawlBatchInfo getBatchInfo()	{ return this.batchInfo; }
	
	protected void processProduct(Product product) {
		try {			
			product.decodeHTTPRepresentation();
			Product existingProduct = this.retailerProductSet.get(product.getId());
			if (existingProduct!=null) {
				logger.debug( "Found match in current DB to  {}/{}/{}. Last updated {}.",
										product.getRetailerId(), product.getId(), 
										product.getRetailerIssuedId(),
										product.getBatchLastUpdated());
				existingProduct.updateTo(product);
				this.toUpdate.put(existingProduct.getId(), existingProduct);
				this.batchInfo.incrementUpdateCount();
			} else {
				this.toCreate.put(product.getId(), product);
				this.batchInfo.incrementCreateCount();
			}
		} catch (Exception exce) {
			logger.error("Unexpected error processing product {}.", product, exce);
			this.processingFailureHandler.processInternalError(product, exce);
			this.batchInfo.incrementInternalFailureCount();
		}
	}
}